#include "head.h"

#define SIZE_BUF_MAX 1024 //定义缓冲区大小为1024字节

//定义文件拷贝的结构体
typedef struct file_str
{
    int src_fd;//源文件的文件描述符
    int desk_fd;//目标文件的文件描述符
    off_t start;//文件拷贝起始位置
    off_t end;//文件拷贝结束位置
}file_str;

//拷贝函数：线程执行的任务
void *copy_file(void *str)
{
    //将传入的参数转换为file_str类型的指针
    file_str *file_str_func = (file_str *)str;
    int src_fd = file_str_func->src_fd;  // 获取源文件的文件描述符
    int desk_fd = file_str_func->desk_fd;  // 获取目标文件的文件描述符
    off_t start = file_str_func->start;  // 获取拷贝起始位置
    off_t end = file_str_func->end;  // 获取拷贝结束位置

    //定义缓冲区，用于临时存储从源文件读取的数据
    char buf[SIZE_BUF_MAX];

    //设置源文件和目标文件的起始位置
    lseek(src_fd,start,SEEK_SET);//移动源文件的指针到起始位置
    lseek(desk_fd,start,SEEK_SET);//移动目标文件的指针到起始位置

    //初始化字节读取和写入计数器
    size_t byte_read = 0;
    size_t byte_write = 0;

    //开始拷贝文件内容，直到拷贝完指定的部分
    while (start < end)
    {
        // 从源文件读取数据到缓冲区
        if ((byte_read = read(src_fd, buf, SIZE_BUF_MAX)) <= 0)
        {
            break;  // 读取失败或文件末尾，退出循环
        }
        
        // 将缓冲区中的数据写入到目标文件
        if ((byte_write = write(desk_fd, buf, byte_read)) <= 0)
        {
            break;  // 写入失败，退出循环
        }

        // 更新起始位置，迭代到下一个位置
        start += byte_write;
    }
    
    printf("拷贝成功\n");
    return NULL;  // 线程执行完毕
}

int main(int argc, char const *argv[])
{
    // 安全判定：确保传入正确的参数个数
    if (4 != argc)
    {
        printf("格式错误 %s dest src\n", argv[0]);  // 格式错误时输出提示
        return 0;
    }

    int pthread_num = atoi(argv[3]);  // 获取要创建的线程数

    // 打开目标文件，指定以写方式打开，若不存在则创建，若存在则清空文件内容
    int desk_fd = open(argv[1], O_WRONLY | O_CREAT | O_TRUNC, 0664);  
    OPEN_ERR(desk_fd);  // 错误处理宏，确保打开成功

    // 打开源文件，以只读方式打开
    int src_fd = open(argv[2], O_RDONLY);
    OPEN_ERR(src_fd);  // 错误处理宏，确保打开成功

    // 获取源文件的状态信息
    struct stat file_stat;  // 文件状态结构体

    // 获取文件的详细状态
    if (fstat(src_fd, &file_stat) < 0)
    {
        perror("fstat err");
        close(desk_fd);  // 关闭文件
        close(src_fd);  // 关闭文件
        return 0;
    }

    // 获取源文件大小，并计算每个线程要处理的部分大小
    off_t file_size = file_stat.st_size;  // 获取文件的大小
    off_t part_size = file_size / pthread_num;  // 每个线程负责的文件大小

    // 定义线程数组
    pthread_t tid[pthread_num];

    // 定义结构体数组，用于存储每个线程的文件信息
    file_str file_str_arr[pthread_num];

    // 创建多个线程并分配工作
    for (int i = 0; i < pthread_num; i++)
    {
        // 计算当前线程负责的起始位置和结束位置
        off_t start = i * part_size;  // 当前线程的起始位置
        off_t end = (i == pthread_num - 1) ? file_size : part_size + start;  // 最后一个线程负责剩余的部分

        // 填充线程相关的文件信息
        file_str_arr[i].desk_fd = desk_fd;
        file_str_arr[i].src_fd = src_fd;
        file_str_arr[i].end = end;
        file_str_arr[i].start = start;

        // 创建线程
        if (0 != pthread_create(&tid[i], NULL, copy_file, (void *)&file_str_arr[i]))
        {
            perror("pthread_create error:");
            close(desk_fd);  // 错误时关闭文件
            close(src_fd);  // 错误时关闭文件
            exit(1);  // 退出程序
        }

        // 设定线程为分离态，主线程无需等待子线程结束
        pthread_detach(tid[i]);
    }

    // 主线程不等待子线程结束，而是等待一段时间
    sleep(5);  // 等待5秒，保证线程有足够时间执行

    // 关闭源文件和目标文件
    close(src_fd);
    close(desk_fd);

    return 0;  // 程序成功结束
}